package monitorcon

import (
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/streadway/amqp"
	"gopkg.in/redis.v4" //redis
	"log"
	"strconv"
	"strings"
	"time"
)

type Monitorcontroller struct {
	//..
	id int
}

const MLABEL string = "M"
const MAX_CONNECTIONNUM_LABEL string = "monitor_maxcons"
const LIVE_CONNECTIONNUM_LABEL string = "liveconnum"
const TOKEN string = "token"
const HOSTNUM string = "hostnum"
const NUMFACTOR float32 = 0.1
const ROUTINGKEY string = "monitordevice"
const QUEUE string = "goqueue"

//redis
var redisclient *redis.Client
var mon *Monitorcontroller

//rabbit mq
var channel *amqp.Channel

func Getredisclient() *redis.Client {
	return redisclient
}

func GetmonitorController() *Monitorcontroller {

	return mon
}

func Setredisclient(rs *redis.Client) {
	redisclient = rs
}

func SetmonitorController(m *Monitorcontroller) {

	mon = m
}

func SetmessageChannel(ch *amqp.Channel) {
	channel = ch
}

func GetmessageChannel() *amqp.Channel {
	return channel
}

//M1                        M2      M3
//  hostnum
//  tokennum
//  hid1:uuid,1 (可以采集)
//  hid2:uuid,0(不可以采集)

//func:分配id，monitor总数增长1
//hostnum=-1 unused conn,
//hostnum >=0 used conn
func (m *Monitorcontroller) Init(rs *redis.Client) bool {
	fmt.Println("init start")
	var inum int
	_, err := rs.Get(MAX_CONNECTIONNUM_LABEL).Result()

	if err != nil {
		log.Println("monitor maxconnection num is not set")

		maxnum := beego.AppConfig.String("maxconnection")       //得到最大连接数
		err := rs.Set(MAX_CONNECTIONNUM_LABEL, maxnum, 0).Err() //设置最大连接数

		if err != nil {
			//panic(err)
			log.Println(err)
			return false
		}

		err2 := rs.Set(LIVE_CONNECTIONNUM_LABEL, "0", 0).Err() //first live connection
		if err2 != nil {
			log.Println(err)
			return false
		}

		imax, err := strconv.Atoi(maxnum)
		if err != nil {
			log.Println(err)
			return false
		}

		m.initconnection(imax, rs)
		inum = 1

	} else {

		log.Println("monitor maxconnection num is already set")
		//得到空闲的连接
		inum = m.getfreeconnection(rs)
		if inum == -1 { //没有空闲连接
			log.Println("all connection is already used")
			return false
		}

	}

	//分配id
	m.id = inum
	log.Printf("id=%d", m.id)
	index := strconv.Itoa(inum)
	rs.HMSet(MLABEL+index, map[string]string{TOKEN: "0"}) //本身token设置为0
	m.updatelivecon(rs, "add")
	return true
}

//live connnum增加1
func (m *Monitorcontroller) updatelivecon(rs *redis.Client, op string) {
	var inum int
	num, err := rs.Get(LIVE_CONNECTIONNUM_LABEL).Result()
	if err != nil {
		log.Println(err)
		return
	}
	inum, err = strconv.Atoi(num)

	if err != nil {
		log.Println(err)
		return
	}

	if op == "add" {
		inum = inum + 1
	} else {

		inum = inum - 1
	}

	liveconnumstr := strconv.Itoa(inum)
	err2 := rs.Set(LIVE_CONNECTIONNUM_LABEL, liveconnumstr, 0).Err() //first live connection
	if err2 != nil {
		log.Println(err)
		return
	}

}

//初始化
func (m *Monitorcontroller) initconnection(maxnum int, rs *redis.Client) {

	var i int
	var connindex string
	for i = 1; i <= maxnum; i++ {

		connindex = MLABEL + strconv.Itoa(i)
		rs.HMSet(connindex, map[string]string{HOSTNUM: "-1"})
		rs.HMSet(connindex, map[string]string{TOKEN: "-1"})
	}

}

//得到空闲的连接
func (m *Monitorcontroller) getfreeconnection(rs *redis.Client) int {
	var i, inum int
	var connindex string
	maxnum, err := rs.Get(MAX_CONNECTIONNUM_LABEL).Result()
	if err != nil {
		log.Println(err)
		return -1
	}

	imaxnum, err := strconv.Atoi(maxnum)
	if err != nil {
		log.Println(err)
		return -1
	}

	for i = 1; i <= imaxnum; i++ {
		connindex = MLABEL + strconv.Itoa(i)
		num, err := rs.HGet(connindex, TOKEN).Result() //得到token ,如果token=-1,则是空闲的连接
		if err != nil {
			continue
		}
		inum, err = strconv.Atoi(num)

		if err != nil {
			log.Println(err)
			return -1
		}

		if inum == -1 { //找到空闲连接
			break
		}
	}

	if i > imaxnum { //没有空闲连接
		//fmt.Println("all connection is used")
		log.Println("all connection is used")
		i = -1
	}
	log.Println("free connection index=", i)
	return i //return index
}

//使用定时器更新token ,采集数据
func (m *Monitorcontroller) Updatetoken(rs *redis.Client) {

	var i, tokennum int
	var connindex string
	maxnum, err := rs.Get(MAX_CONNECTIONNUM_LABEL).Result()
	if err != nil {
		log.Println(err)
		return
	}

	imaxnum, err := strconv.Atoi(maxnum)
	if err != nil {
		log.Println(err)
		return
	}

	for i = 1; i <= imaxnum; i++ {

		connindex = MLABEL + strconv.Itoa(i)
		num, err := rs.HGet(connindex, TOKEN).Result() //得到token ,如果token=-1,则是空闲的连接

		if err != nil {
			//fmt.Printf("%s token is no existed\n",connindex)
			continue
		}

		tokennum, err = strconv.Atoi(num)

		if err != nil {
			log.Println(err)
			return
		}

		if tokennum == -1 {
			continue //是空闲连接，无需update token
		}

		//log.Printf("index=%d\n",i)
		if m.id == i { //是自身连接, update token=0
			//need redis watch ,保证操作成功

			//m.setselfconnection(rs,connindex)
			rs.HSet(connindex, TOKEN, "0")
			log.Printf("self %s token=0\n", connindex)

			//采集数据
			m.getinterfacedata(i, rs)
		} else { //update other connection token , token+1

			tokennum = tokennum + 1

			var livenum int

			num, err := rs.Get(LIVE_CONNECTIONNUM_LABEL).Result() //得到当前live连接数
			if err != nil {
				log.Println(err)
				return
			}
			livenum, err = strconv.Atoi(num)

			var upperlimit float32
			upperlimit = float32(imaxnum) * NUMFACTOR
			upperlimit = upperlimit * float32(livenum) //上线= maxnum*factor*livenum
			log.Println("upper limit", upperlimit)
			if tokennum >= int(upperlimit) { //重置为空闲的连接
				rs.HMSet(connindex, map[string]string{TOKEN: "-1"})

				m.reallocate(i, rs)
				rs.HMSet(connindex, map[string]string{HOSTNUM: "-1"})

				log.Printf("%s connection is reset\n", connindex)
				m.updatelivecon(rs, "del") //live conn num -1
			} else {

				_, err := rs.HGet(connindex, TOKEN).Result() //得到token ,如果token=-1,则是空闲的连接
				if err != nil {
					log.Printf("%s token is no existed\n", connindex)
					continue
				}

				tokennumstr := strconv.Itoa(tokennum)
				rs.HSet(connindex, TOKEN, tokennumstr)
				log.Printf("%s token=%s\n", connindex, tokennumstr)

			}
		}

	}
}

//采集接口数据
func (m *Monitorcontroller) getinterfacedata(index int, rs *redis.Client) {

	connindex := MLABEL + strconv.Itoa(index)
	num, err := rs.HGet(connindex, HOSTNUM).Result() //得到token ,如果token=-1,则是空闲的连接

	if err != nil {
		//fmt.Printf("%s token is no existed\n",connindex)
		return
	}

	hostnum, err := strconv.Atoi(num)

	if err != nil {
		log.Println(err)
		return
	}

	if hostnum == -1 {
		return //没有可分配的设备
	}

	var i int
	for i = 1; i <= hostnum; i++ {

		hi := strconv.Itoa(i)

		hostinfo, err := rs.HGet(connindex, "hid"+hi).Result() //得到token ,如果token=-1,则是空闲的连接

		if err != nil {

			continue
		}

		s := strings.Split(hostinfo, ",")
		log.Printf("%s,%s\n", s[0], s[1])

		if s[1] == "1" { //需要采集数据
			var resp string
			resp = Getstackinfo(s[0]) //从服务器返回信息
			log.Println(resp)

			//save data to redis server
			Savehostinfotoredis(rs, s[0], "stack", resp)

			//send data to mq queue

			msg := amqp.Publishing{
				DeliveryMode: amqp.Persistent,
				Timestamp:    time.Now(),
				ContentType:  "text/plain",
				Body:         []byte(resp),
			}
			//"key"

			channel.Publish(QUEUE, ROUTINGKEY, false, false, msg)

			//save data to db
			map2 := make(map[string]interface{})
			err = json.Unmarshal([]byte(resp), &map2)
			//err = json.Unmarshal( []byte(s), &map2)

			if err != nil {
				log.Println("unmarshal json info failed")
				return

			}

			log.Println(map2)

			var arr [10]string //define 至多10个节点的数组
			arr[0] = ""        //第一个根节点为空
			Parsemap(s[0], map2, arr, 1, "stack")
			log.Println("end saving data")

		}

	}

}

//重新分配设备
//index: 连接设备的connection index

func (m *Monitorcontroller) reallocate(index int, rs *redis.Client) {
	connindex := MLABEL + strconv.Itoa(index)
	num, err := rs.HGet(connindex, HOSTNUM).Result() //得到token ,如果token=-1,则是空闲的连接

	if err != nil {
		//fmt.Printf("%s token is no existed\n",connindex)
		return
	}

	hostnum, err := strconv.Atoi(num)

	if err != nil {
		log.Println(err)
		return
	}

	if hostnum == -1 {
		return //没有可分配的设备
	}

	var i int
	for i = 1; i <= hostnum; i++ {

		hi := strconv.Itoa(i)

		hostinfo, err := rs.HGet(connindex, "hid"+hi).Result() //得到token ,如果token=-1,则是空闲的连接

		if err != nil {

			continue
		}

		s := strings.Split(hostinfo, ",")
		log.Printf("%s,%s\n", s[0], s[1])

		rs.HDel(connindex, "hid"+hi)   //删除hid节点
		m.RegisterHost(rs, s[0], s[1]) //重新分配节点

	}

}

//分配host,
//第一个hid1,以后依次为hid2,hid3，hidXXX
//首先分配hostnum=-1 的连接，以后会选择hostnum最小的连接
func (m *Monitorcontroller) RegisterHost(rs *redis.Client, hostuuid string, optype string) {

	var i, tokennum int
	var connindex string
	maxnum, err := rs.Get(MAX_CONNECTIONNUM_LABEL).Result()
	if err != nil {
		log.Println(err)
		return
	}

	imaxnum, err := strconv.Atoi(maxnum)
	if err != nil {
		log.Println(err)
		return
	}

	var minhostnum, minindex int
	minhostnum = 100 //初始化为最大值100
	for i = 1; i <= imaxnum; i++ {

		connindex = MLABEL + strconv.Itoa(i)
		num, err := rs.HGet(connindex, TOKEN).Result() //得到token ,如果token=-1,则是空闲的连接

		if err != nil {
			//fmt.Printf("%s token is no existed\n",connindex)
			continue
		}

		tokennum, err = strconv.Atoi(num)

		if err != nil {
			log.Println(err)
			return
		}

		if tokennum == -1 {
			continue //是空闲连接，不能分配host
		}

		if tokennum >= 0 { //正在使用的token
			hnum, err := rs.HGet(connindex, HOSTNUM).Result() //查看已经分配的hostnum
			if err != nil {

				continue
			}

			hostnum, err := strconv.Atoi(hnum)
			if err != nil {

				continue
			}

			if hostnum == -1 { //选择成为最小的hostnum,分配当前的host
				rs.HSet(connindex, HOSTNUM, "1")
				rs.HSet(connindex, "hid1", hostuuid+","+optype) //default is 0, 初始化为不可以采集数据
				return                                          //结束分配
			} else {
				if hostnum < minhostnum {
					minhostnum = hostnum
					minindex = i
				}
			}

		}
	}

	if minhostnum != 100 {
		connindex = MLABEL + strconv.Itoa(minindex)
		//分配设备，minhostnum增加1
		minhostnum = minhostnum + 1
		mnum := strconv.Itoa(minhostnum)

		rs.HSet(connindex, "hid"+mnum, hostuuid+","+optype) //初始化为不可以采集数据,重新分配的可能为1
		rs.HSet(connindex, HOSTNUM, mnum)                   //控制的设备数目增长1
	}

}

//打开数据采集
func (m *Monitorcontroller) OpenHost(rs *redis.Client, hostuuid string) bool {
	return OpHost(rs, hostuuid, "1")
}

//关闭数据采集
func (m *Monitorcontroller) CloseHost(rs *redis.Client, hostuuid string) bool {
	return OpHost(rs, hostuuid, "0")
}

//open or close device
func OpHost(rs *redis.Client, hostuuid string, op string) bool {

	var i, j, tokennum int
	var connindex, hindex string

	maxnum, err := rs.Get(MAX_CONNECTIONNUM_LABEL).Result()
	if err != nil {
		log.Println(err)
		return false
	}

	imaxnum, err := strconv.Atoi(maxnum)
	if err != nil {
		log.Println(err)
		return false
	}

	for i = 1; i <= imaxnum; i++ {

		connindex = MLABEL + strconv.Itoa(i)
		num, err := rs.HGet(connindex, TOKEN).Result() //得到token ,如果token=-1,则是空闲的连接

		if err != nil {
			//fmt.Printf("%s token is no existed\n",connindex)
			continue
		}

		tokennum, err = strconv.Atoi(num)

		if err != nil {
			log.Println(err)
			return false
		}

		if tokennum == -1 {
			continue //是空闲连接，不能分配host
		}

		if tokennum >= 0 { //正在使用的token
			hnum, err := rs.HGet(connindex, HOSTNUM).Result() //查看已经分配的hostnum
			if err != nil {

				continue
			}

			hostnum, err := strconv.Atoi(hnum)
			if err != nil {

				continue
			}

			if hostnum == -1 {
				continue
			}

			for j = 1; j <= hostnum; j++ {

				hindex = strconv.Itoa(j)

				hostinfo, err := rs.HGet(connindex, "hid"+hindex).Result()

				if err != nil {
					continue
				}

				s := strings.Split(hostinfo, ",")
				log.Printf("%s,%s\n", s[0], s[1])

				if s[0] == hostuuid { //定位到需要的hostuuid
					s[1] = op //重置操作, 1:open host,0:closehost
					rs.HSet(connindex, "hid"+hindex, s[0]+","+s[1])
					return true
				}
			}

		}
	}

	return false //在redis内没有找到设备的hostuuid
}
